package com.gx.common.aop;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.gx.common.anntation.Log;
import com.gx.common.result.AppResult;
import com.gx.pojo.SysLogInfo;
import com.gx.service.SysLogInfoService;
import com.gx.utils.HttpContextUtils;
import com.gx.utils.IpUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.ArrayList;

@Aspect
@Component
@Slf4j
public class LogAspect {

    @Resource
    private SysLogInfoService sysLogInfoService;

    @Pointcut("@annotation(com.gx.common.anntation.Log)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        Object result = point.proceed();
        long time = System.currentTimeMillis() - beginTime;
        try {
            saveLog(point, time, result);
        } catch (Exception e) {
            e.printStackTrace();
            log.warn("日志保存失败!");
        }
        return result;
    }

    public void saveLog(ProceedingJoinPoint joinPoint, long time, Object result) {
        // 获取request
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log logAnnotation = method.getAnnotation(Log.class);
        //类名
        String className = joinPoint.getTarget().getClass().getName();
        //方法名
        String methodName = method.getName();
        // 请求的方法参数名称
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        //参数
        String[] paramNames = u.getParameterNames(method);
        Class<?>[] parameterTypes = method.getParameterTypes();
        // 请求的方法参数值
        Object[] args = joinPoint.getArgs();
        String ip = IpUtil.getIp(request);
        String browserName = IpUtil.getBrowserName(request);
        String requestURI = request.getRequestURI();
        String description = logAnnotation.description();
        String type = logAnnotation.type();
        if (!browserName.contains("Unknown")) {
            SysLogInfo sysLoginfo = new SysLogInfo();
            sysLoginfo.setDescription(description);
            sysLoginfo.setType(type);
            sysLoginfo.setIp(ip);
            sysLoginfo.setOs(browserName);
            sysLoginfo.setClassName(className);
            sysLoginfo.setTime(time + "ms");
            sysLoginfo.setRequestUrl(requestURI);
            sysLoginfo.setMethodName(methodName);
            sysLoginfo.setCreatedTime(DateUtil.now());
            ArrayList<JSONObject> param = new ArrayList<>();
            AppResult<?> isOk = new AppResult<>();
            //登录单独处理,敏感信息参数置空,可根据参数,token进行特殊处理日志
            try {
                //对返回结果转换为自己项目的全局返回即可，此处为了抓是否成功
                isOk = (AppResult<?>) result;
            } catch (Exception e) {
                log.warn("结果状态保存失败!");
            }
            Integer code = isOk.getCode();
            sysLoginfo.setCode(code);
            if (paramNames != null) {
                //登录接口需要单独处理，抓取参数密码最好置空
                if (StrUtil.equals("login", methodName)) {
                    for (String paramName : paramNames) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put("paramName", paramName);
                        jsonObject.put("paramValue", "");
                        param.add(jsonObject);
                        sysLoginfo.setParams(JSONUtil.toJsonStr(param));
                        if (code == 200) {
                            JSONObject data = (JSONObject) isOk.getData();
                            //获取登录返回的用户信息
                            sysLoginfo.setUserId(1);
                            sysLoginfo.setNickName("测试登录用户");
                        }
                    }
                } else {
                    for (int i = 0; i < paramNames.length; i++) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put("paramName", paramNames[i]);
                        jsonObject.put("paramValue", args[i]);
                        param.add(jsonObject);
                    }
                    sysLoginfo.setParams(JSONUtil.toJsonStr(param));
                    //获取当前操作人例如通过token，redis，本地线程
                    sysLoginfo.setUserId(1);
                    sysLoginfo.setNickName("测试昵称");
                }
            }

            log.info("日志抓取结果:{}", JSONUtil.toJsonStr(sysLoginfo));
            sysLogInfoService.save(sysLoginfo);
        }
    }
}